//
// Created by Administrator on 2025/7/8.
//

#include "stm32flash.h"

#include "stm32f1xx_hal.h"

#include <string.h>

uint32_t write_cache[FLASH_PAGE_SIZE / 4];

int flash_erase(uint32_t addr, uint32_t size) {
    if (size == 0 || size < FLASH_PAGE_SIZE) {
        return -1;
    }

    if (addr < FLASH_BASE || addr > FLASH_BANK1_END) {
        return -1;
    }


    HAL_FLASH_Unlock();

    FLASH_EraseInitTypeDef init;

    uint32_t pages = size / FLASH_PAGE_SIZE;
    uint32_t error;

    init.TypeErase   = FLASH_TYPEERASE_PAGES;
    init.PageAddress = addr;
    init.NbPages     = pages;
    if (HAL_FLASHEx_Erase(&init, &error) != HAL_OK) {
        HAL_FLASH_Lock();
        return -1;
    }

    HAL_FLASH_Lock();
    return 0;
}

uint32_t flash_read(uint32_t addr, uint32_t *data, uint32_t size) {
    if (size == 0 || data == NULL) {
        return 0;
    }

    if (addr < FLASH_BASE || addr > FLASH_BANK1_END) {
        return 0;
    }

    uint32_t len = size;

    while (len) {
        *(data++) = *(__IO uint32_t *) (addr);
        addr += 4;
        len -= 4;
    }

    return size;
}

uint32_t flash_write(uint32_t addr, const uint32_t *data, uint32_t size) {
    if (size == 0 || data == NULL) {
        return 0;
    }

    if (addr < FLASH_BASE || addr > FLASH_BANK1_END) {
        return 0;
    }

    HAL_FLASH_Unlock();

//    uint32_t len = size & (~0x3);
    uint32_t len = size;
    while (len) {
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, *data) != HAL_OK) {
            break;
        }
        data++;
        addr += 4;
        len -= 4;
    }

    HAL_FLASH_Lock();

    return size - len;
}

uint32_t flash_write_safe(uint32_t addr, uint32_t *data, uint32_t size) {
    if (addr < FLASH_BASE || addr > FLASH_BANK1_END) {
        return 0;
    }

    uint32_t start_page_addr = addr / FLASH_PAGE_SIZE;
    start_page_addr = start_page_addr * FLASH_PAGE_SIZE;

    /** addr before data cache write */
    uint32_t before_size = 0;
    if (addr > start_page_addr) {
        before_size = start_page_addr + FLASH_PAGE_SIZE - addr;
        before_size = (before_size > size ? size : before_size);

        memset(write_cache, 0xFF, FLASH_PAGE_SIZE);
        flash_read(start_page_addr, write_cache, FLASH_PAGE_SIZE);
        memcpy(&write_cache[(addr - start_page_addr) / 4], data, before_size);

        flash_erase(start_page_addr, FLASH_PAGE_SIZE);
        flash_write(start_page_addr, write_cache, FLASH_PAGE_SIZE);
    }
    addr += before_size;
    size -= before_size;
    data += before_size;

    if (size == 0) {
        return before_size;
    }

    uint32_t full_size = (size / FLASH_PAGE_SIZE) * FLASH_PAGE_SIZE;
    uint32_t left_size = size - full_size;

    uint32_t end_page_addr = addr + full_size;

    /** program full page */
    flash_erase(addr, full_size);
    flash_write(addr, data, full_size);
    addr += full_size;
    size -= full_size;
    data += full_size;

    if (size == 0) {
        return before_size + full_size;
    }

    /** left data program */
    if (left_size > 0) {
        memset(write_cache, 0xFF, FLASH_PAGE_SIZE);
        flash_read(end_page_addr, write_cache, FLASH_PAGE_SIZE);
        memcpy(write_cache, data, left_size);

        flash_erase(end_page_addr, FLASH_PAGE_SIZE);
        flash_write(addr, write_cache, FLASH_PAGE_SIZE);
    }


    return before_size + full_size + left_size;
}

